package com.bmc.arsys.demo.javadriver;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Stack;

import com.bmc.arsys.api.Constants;
import com.bmc.arsys.api.ProxyManager;
import com.bmc.arsys.api.StatusInfo;

/**
 * Performance test for JavaDriver
 * @author Yucheng Huang
 * May 29, 2007
 */
public class PerfJavaDriver extends JavaDriver {
    static long OUT_MODE_PERF_LOG = 0x0002;

    static long OUT_MODE_PERF_LOG_MEM = 0x0004;

    static long OUT_MODE_ANY = 0x0007;

    private static long outputSetting;

    static int fileCount = 0;

    static long driverStartTime;

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            ProxyManager.setUseConnectionPooling(false);
            PerfJavaDriver driver = new PerfJavaDriver();
            driver.setCommandLineArgs(args);
            driver.setPrimaryThread(true);
            javaDriverOnly = false;
            driver.start();
        } catch (Exception e) {
            outputWriter.printString("Error in executing the command\n");
        }
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#initThreadControlBlockPtr()
     */
    @Override
    protected ThreadControlBlock initThreadControlBlockPtr() {
        localStorage = new PerfThreadControlLocalStorage();
        return (ThreadControlBlock) localStorage.get();
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#initCommandProcessing()
     */
    @Override
    public void initCommandProcessing() {
        super.initCommandProcessing();
        if ((getOutputSetting() & (OUT_MODE_PERF_LOG | OUT_MODE_PERF_LOG_MEM)) != 0) {
            // Set the start time
            driverStartTime = System.currentTimeMillis();

            // initialize the value that we use to generate suffixes for the
            // log file names because it may have been set with a command
            // line argument
            if (isForOutputFile()) {
                setFileCount(getOutputCount());
            }
        }

    }

    protected static boolean isForOutputFile() {
        boolean flag = false;
        //these two settings are exclusive. If OUT_MODE_PERF_LOG_MEM will not write to file at all.
        if (((PerfJavaDriver.getOutputSetting() & PerfJavaDriver.OUT_MODE_PERF_LOG) != 0)
                && ((PerfJavaDriver.getOutputSetting() & PerfJavaDriver.OUT_MODE_PERF_LOG_MEM) == 0)) {
            flag = true;
        }
        return flag;
    }

    static synchronized void setFileCount(int count) {
        fileCount = count;
    }

    static synchronized int getAndIncrementTheFileCount() {
        return fileCount++;
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#logResult(java.util.List, long)
     */
    @Override
    protected void logResult(List<StatusInfo> statusList, long startTime) throws IOException {
        super.logResult(statusList, startTime);
        // process if we are to make a performance output file entry
        if ((getOutputSetting() & (OUT_MODE_PERF_LOG | OUT_MODE_PERF_LOG_MEM)) != 0) {
            long errorCode = 0;
            if ((statusList != null) && (statusList.size() != 0)) {
                if (statusList.get(0).getMessageType() == Constants.AR_RETURN_ERROR) {
                    errorCode = statusList.get(0).getMessageNum();
                }
            }
            logPerfResults(errorCode, startTime);
        }
    }

    protected void logPerfResults(long errorCode, long startTime) throws IOException {
        PerfThreadControlBlock threadControlBlockObj = (PerfThreadControlBlock) getThreadControlBlockPtr();
        // process if this is the first logging we've done for the
        // current thread
        if (!threadControlBlockObj.isResultFileOpened()) {
            threadControlBlockObj.initLogMethod();
        }

        // calculate the start time of the current operation which we are
        // representing as the elapsed time from the start of the test
        long elapsedTimeMilliSec = startTime - driverStartTime;

        //  process depending if the intention is to log the results to memory
        //  or directly to a file

        String statString = getStatString(elapsedTimeMilliSec, threadControlBlockObj.getCurrentCommand(), errorCode,
                threadControlBlockObj.getAuxCount());
        threadControlBlockObj.writeInResultFile(statString);
    }

    static String getStatString(long startTime, String command, long errorCode, long endTime) {
        String stTime = "" + startTime;
        int i = 0;
        StringBuilder prefix = new StringBuilder();
        for (i = stTime.length(); i < 8; i++) {
            prefix.append("0");
        }
        StringBuilder statString = new StringBuilder();
        statString.append(prefix.toString()).append(stTime).append(" ").append(command);

        for (i = command.length(); i < 6; i++) {
            statString.append(" ");
        }
        statString.append(" ");

        String errString = "" + errorCode;
        StringBuilder prefixErr = new StringBuilder();
        for (i = errString.length(); i < 10; i++) {
            prefixErr.append(" ");
        }
        statString.append(prefixErr.toString()).append(errString).append(" ");

        String endString = "" + endTime * 1000;
        StringBuilder prefixEnd = new StringBuilder();
        for (i = endString.length(); i < 10; i++) {
            prefixEnd.append(" ");
        }

        statString.append(prefixEnd.toString()).append(endString);

        return statString.toString();
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#destroyThreadControlBlockPtr()
     */
    @Override
    protected void destroyThreadControlBlockPtr(boolean noWait) throws IOException {
        try {
            super.destroyThreadControlBlockPtr(noWait);
        } finally {
            processResultFile();
        }
    }

    protected void processResultFile() throws IOException {
        // get the control block pointer for the current thread
        PerfThreadControlBlock threadControlBlockObj = (PerfThreadControlBlock) getThreadControlBlockPtr();
        try {
            Stack<String> newStack = new Stack<String>();
            if ((getOutputSetting() & OUT_MODE_PERF_LOG_MEM) != 0) {
                Stack<String> resultStack = threadControlBlockObj.getResultStack();

                // return if there is no stack or the stack is empty
                if ((resultStack != null) && !resultStack.empty()) {
                    // write each of the log records we recorded in memory to the file
                    // and then free the memory allocated for it
                    while (!resultStack.empty()) {
                        String tmp = (String) resultStack.pop();
                        newStack.push(tmp);
                    }
                }
            }
            if (isForOutputFile()) {
                // write a comment into the file that allows this file to be
                // cross-referenced with its corresponding output file
                PrintWriter fp = threadControlBlockObj.getResultFile();
                if (threadControlBlockObj.getOutputFileName() != null) {
                    if (fp != null) {
                        fp.println();
                        fp.println("Corresponding Output File: " + threadControlBlockObj.getOutputFileName());
                        fp.flush();
                    }
                }

                while (!newStack.empty()) {
                    String tmp = (String) newStack.pop();
                    fp.println(tmp);
                }

            }
        } finally {
            // Close the result file
            threadControlBlockObj.closeResultFile();
            threadControlBlockObj.closeResultStack();
        }
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#setOutputCount(java.lang.String)
     */
    @Override
    protected void setOutputCount(String tempPtr) {
        PerfThreadControlBlock perfThreadControlBlockPtr = (PerfThreadControlBlock) getThreadControlBlockPtr();
        perfThreadControlBlockPtr.setResultFileCount(new Integer(tempPtr).intValue());
    }

    /**
     * @return the outputCount
     */
    protected int getOutputCount() {
        PerfThreadControlBlock perfThreadControlBlockPtr = (PerfThreadControlBlock) getThreadControlBlockPtr();
        return perfThreadControlBlockPtr.getResultFileCount();
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#printAuxillaryStatus()
     */
    @Override
    void printAuxillaryStatus() {
        PerfThreadControlBlock threadControlBlockObj = (PerfThreadControlBlock) getThreadControlBlockPtr();
        outputWriter.printResult("Elapsed micro seconds: " + (threadControlBlockObj.getAuxCount() * 1000) + "\n");
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#InstantiateJavaDriver()
     */
    @Override
    protected JavaDriver instantiateJavaDriver() {
        return new PerfJavaDriver();
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#beginAPICall()
     */
    @Override
    public void beginAPICall() {
        if ((getOutputSetting() & OUT_MODE_ANY) != 0) {
            PerfThreadControlBlock threadControlBlockObj = (PerfThreadControlBlock) getThreadControlBlockPtr();
            threadControlBlockObj.setAuxCount(System.currentTimeMillis());
        }
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#endAPICall(java.util.List)
     */
    @Override
    public void endAPICall(List<StatusInfo> statusList) {
        try {
            if ((getOutputSetting() & OUT_MODE_ANY) != 0) {
                PerfThreadControlBlock threadControlBlockObj = (PerfThreadControlBlock) getThreadControlBlockPtr();

                long startTime = threadControlBlockObj.getAuxCount();

                // Set the time taken in the auxilary count
                threadControlBlockObj.setAuxCount(System.currentTimeMillis() - startTime);

                logResult(statusList, startTime);
            }
        } catch (Exception e) {
            System.out.println("Problem in end API Call...");
        }
    }

    protected static long getOutputSetting() {
        return outputSetting;
    }

    /* (non-Javadoc)
     * @see com.bmc.arsys.demo.javadriver.JavaDriver#setOutputSetting(java.lang.String)
     */
    @Override
    protected void setOutputSetting(String tempPtr) {
        super.setOutputSetting(tempPtr);
        outputSetting = new Long(tempPtr).longValue();
    }

}

class PerfThreadControlLocalStorage extends ThreadControlLocalStorage {

    @SuppressWarnings("unchecked")
    protected Object initialValue() {
        PerfThreadControlBlock threadControlBlockPtr = new PerfThreadControlBlock();
        set(threadControlBlockPtr);
        return get();
    }
}
